package com.wuhao.code.check.model

import com.aegis.kotlin.containsAny
import com.aegis.mybatis.xmlless.AnnotatedElementDetailInfo
import com.aegis.mybatis.xmlless.ElementInfoBuilder
import com.aegis.mybatis.xmlless.annotations.JsonResult
import com.aegis.mybatis.xmlless.annotations.PropertyMapping
import com.aegis.mybatis.xmlless.constant.ClassNames
import com.aegis.mybatis.xmlless.model.FieldMappingList
import com.aegis.mybatis.xmlless.model.XmllessTableInfo
import com.aegis.mybatis.xmlless.model.base.BaseClassWrapper
import com.aegis.mybatis.xmlless.model.base.BaseXmllessMethodWrapper
import com.aegis.mybatis.xmlless.model.criteria.ParameterInfo
import com.aegis.mybatis.xmlless.resolver.MappingResolver
import com.aegis.mybatis.xmlless.util.TableInfoProvider
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.source.PsiClassReferenceType
import com.wuhao.code.check.getStringAnnoValue
import com.wuhao.code.check.hasAnnotation
import com.wuhao.code.check.model.base.BasePsiMethodWrapper
import org.apache.ibatis.annotations.Param

/**
 *
 * @author 吴昊
 * @date 2024/1/18 9:53
 * @since v0.0.0
 * @version 1.0
 */
class PsiMethodWrapper(
    method: PsiMethod,
    private val modelClass: BaseClassWrapper,
    private val tableInfo: XmllessTableInfo
) : BasePsiMethodWrapper<PsiMethod>(method) {

  private fun getRealType(): PsiElement? {
    val typeEl = fn.returnTypeElement?.type
    if (typeEl is PsiClassReferenceType) {
      val psiClass = typeEl.resolve()
      val interfaces = psiClass?.getAllSuperClassNames() ?: listOf()
      val hasGenericType = interfaces.containsAny(
          listOf(
              ClassNames.JAVA_COLLECTION,
              ClassNames.SPRING_DATA_PAGE,
              ClassNames.MYBATIS_PLUS_PAGE
          )
      )
      if (hasGenericType) {
        val arg = typeEl.typeArguments().first()
        if (arg is PsiClassReferenceType) {
          return arg.reference.resolve()
        }
      }
      return psiClass
    }
    return null
  }

  override fun resolveReturnMappingList(): FieldMappingList? {
    val realType = getRealType()
    if (realType is PsiClass) {
      val wrapper = PsiClassWrapper(realType)
      return if (wrapper != modelClass
          && !fn.hasAnnotation(JsonResult::class.java)
          && wrapper.isComplex()
      ) {
        val currentTableInfo = TableInfoProvider.getOrInitXmllessTableInfo(wrapper)
        MappingResolver.resolveNonEntityClass(
            wrapper, wrapper.getClassName(), tableInfo, currentTableInfo
        )
      } else {
        null
      }
    }
    return null
  }

  override fun getReturnType(): Class<*>? {
    return null
  }

  override fun getParameters(): List<ParameterInfo> {
    return resolveParameters()
  }

  override fun getXmllessMethod(): BaseXmllessMethodWrapper {
    return PsiXmllessMethodWrapper(fn)
  }

  override fun getPropertiesMappingInner(): Map<String, String> {
    val typeEl = fn.returnTypeElement
    if (typeEl is PsiClassReferenceType) {
      val psiClass = typeEl.resolve()
      if (psiClass is PsiClass) {
        return psiClass.allFields.filter {
          it.hasAnnotation(PropertyMapping::class.java.name)
        }.associate {
          it.name to it.getStringAnnoValue(PropertyMapping::class.java, PropertyMapping::value)!!
        }
      }
    }
    return mapOf()
  }

  override fun getFullName(): String {
    return fn.containingClass?.qualifiedName + "." + fn.name
  }

  override fun getName(): String {
    return fn.name
  }

  override fun getOptionalParameterNames(): Array<String> {
    return fn.parameterList.parameters.map {
      it.getStringAnnoValue(Param::class.java.name, "value") ?: it.name
    }.toTypedArray()
  }

  override fun getModelFields(): List<AnnotatedElementDetailInfo> {
    return modelClass.getAllFields()
  }

  override fun getModelName(): String {
    return modelClass.getClassName()
  }

  private fun resolveParameters(): List<ParameterInfo> {
    return fn.parameterList.parameters.map {
      val detail = ElementInfoBuilder.build(PsiParameterWrapper(it))
      ParameterInfo(
          detail,
          it.name,
          detail.typeInfo,
          detail.chooseCriteria
      )
    }
  }

}
